/*
    KMC Simulation
    Copyright (c) 2012 Filip Rozbořil

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
    
    Based on the original implementation by Matsumoto & Nishimura
    
    Copyright (C) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura,
	 All rights reserved.
*/

#include "Random.h"
#include "includes.h"

class MersenneTwister : public Random
{
	private:
		static const int n = 624, m = 397;
		void seed(uint32_t); // seed with 32 bit integer
		void seed(const uint32_t*, int size); // seed with array
		uint32_t state[n];
		int p; // position in state array
		uint32_t twiddle(uint32_t, uint32_t); // used by generateState()
		void generateState(); // generate new state
			
	public:
		// constructor with 32 bit int as seed
	  	MersenneTwister(uint32_t s) { seed(s); }
		// constructor with array of size 32 bit ints as seed
	  	MersenneTwister(const uint32_t* array, int32_t size) { seed(array, size); }
	  	~MersenneTwister() { }
		uint32_t getInteger();
};

inline uint32_t MersenneTwister :: twiddle(uint32_t u, uint32_t v)
{
	return (((u & UINT32_C(0x80000000)) | (v & UINT32_C(0x7FFFFFFF))) >> 1) ^ ((v & UINT32_C(1)) ? UINT32_C(0x9908B0DF) : UINT32_C(0x0));
}

inline uint32_t MersenneTwister :: getInteger() 
{ // generate 32 bit random int
	if (p == n) generateState(); // new state vector needed
	// gen_state() is split off to be non-inline, because it is only called once
	// in every 624 calls and otherwise irand() would become too big to get inlined
	uint32_t x = state[p++];
	x ^= (x >> 11);
	x ^= (x << 7) & UINT32_C(0x9D2C5680);
	x ^= (x << 15) & UINT32_C(0xEFC60000);
	return x ^ (x >> 18);
}